package example.com.dynamicproxy.AMSHook;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Handler;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;

/**
 * Created by Bolex on 2017/6/20.
 */
//https://www.zhihu.com/question/37929529/answer/80930823
/*
http://android.jobbole.com/83157/
说明为什么要amnClazz.getDeclaredField("gDefault")

 */
public class AMSHookUtil {

    @SuppressLint("StaticFieldLeak")
    private static Context sContext;
    private static String sPackageName;
    private static String sHostClazzName;
    private static final String TAG = "AMSHookUtil";


    /**
     *   * 这里我们通过反射获取到AMS的代理本地代理对象
     * Hook以后动态串改Intent为已注册的来躲避检测
     * @param context            上下文
     */
    public static void hookStartActivity(Context context) {
        if (context == null || sContext != null) {
            return;
        }
        try {
            Context applicationContext = context.getApplicationContext();
            sContext = applicationContext;
            PackageManager manager = applicationContext.getPackageManager();
            sPackageName = applicationContext.getPackageName(); //com.ttls.gpscurrentlocation
            PackageInfo packageInfo = manager.getPackageInfo(sPackageName, PackageManager.GET_ACTIVITIES);
            ActivityInfo[] activities = packageInfo.activities;
            if (activities == null || activities.length == 0) {
                return;
            }
            ActivityInfo activityInfo = activities[0]; //MainActivity,HostActivity
            sHostClazzName = activityInfo.name;
            //Log.e(TAG,"pkgName:" + sPackageName + "\tHostClazzName:" + sHostClazzName);

            //framework使用了一个单例把这个AMS的代理对象保存了起来，getDeclaredField("gDefault")就是获取该单例gDefault
            Class<?> classActivityManagerNactive = Class.forName("android.app.ActivityManagerNative");
            Field field_gDefault = classActivityManagerNactive.getDeclaredField("gDefault");
            
            //获取这个field的真正内容gDefault对象(是ActivityManagerNative)
            field_gDefault.setAccessible(true);//gDefault为private变量，故调用private变量的方法之前必须进行此操作
            Object gDefault = field_gDefault.get(null); //gDefault为static对象，所有静态对象的反射可以通过传null获取。如果是实列必须传实例

            //gDefault是一个android.util.Singleton对象; 取出mInstance字段(是ActivityManagerProxy)
            Class<?> classSingleton = Class.forName("android.util.Singleton");
            Field field_mInstance = classSingleton.getDeclaredField("mInstance");
            field_mInstance.setAccessible(true);
            Object mInstance = field_mInstance.get(gDefault);

            //生成动态代理(实际上是ActivityManagerProxy)
            Object dynamicProxy = Proxy.newProxyInstance(context.getClass().getClassLoader(),
                    mInstance.getClass().getInterfaces(),
                    new HookInvocationHandler(mInstance, sPackageName, sHostClazzName));
          /*
            注意：通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象，它并不是我们的InvocationHandler类型，
                 也不是我们定义的那组接口的类型，而是在运行是动态生成的一个对象，并且命名方式都是这样的形式，
                 以$开头，proxy为中，最后一个数字表示对象的标号。

                 --能否再动态，调用；直接用mInstance调用
             */

            //替换mInstance为动态代理dynamicProxy (改变framework的内容)
            field_mInstance.set(gDefault, dynamicProxy);

            hookActivityThreadHandler();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * hook ActivityThread  handle 在这里我们需要替换我们未被注册的Activity Intent
     *
     * @throws Exception
     */
    private static void hookActivityThreadHandler() throws Exception {
        Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
        Field sCurrentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
        sCurrentActivityThreadField.setAccessible(true);
        Object sCurrentActivityThreadObj = sCurrentActivityThreadField.get(null);
        Field mHField = activityThreadClazz.getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mH = (Handler) mHField.get(sCurrentActivityThreadObj);
        Field callBackField = Handler.class.getDeclaredField("mCallback");
        callBackField.setAccessible(true);
        callBackField.set(mH, new ActivityThreadHandlerCallBack());
    }


}
